Shader Internals

gl_VertexIndex

  • Behavior depends on draw type

  • Non-indexed draw

    vkCmdDraw(vertexCount, instanceCount, firstVertex, firstInstance);
    
    gl_VertexIndex = firstVertex + (0 .. vertexCount-1)
    
  • Non-indexed draw with VK_NULL_HANDLE :

    // Non-indexed
    vk.CmdDrawIndirect(
        cmd,
        _game.indirect_buffer[frame_idx].handle,
        0,
        2,
        size_of(vk.DrawIndirectCommand)
    )
    
    gl_VertexIndex = (0 .. indexCount-1)
    
    • Index Draw + VK_NULL_HANDLE : gl_VertexIndex  doesn't work.

    • Non-Index Draw + VK_NULL_HANDLE : gl_VertexIndex  works; yea....

  • Indexed draw:

    vkCmdDrawIndexed(indexCount, instanceCount, firstIndex, vertexOffset, firstInstance);
    
    gl_VertexIndex = vertexOffset + indexBuffer[firstIndex + i]
    
  • Indexed draw with VK_NULL_HANDLE  index buffer + maintenance6

    // Indexed
    vk.CmdBindIndexBuffer(cmd, 0, 0, .UINT32)
    vk.CmdDrawIndexedIndirect(
        cmd,
        _game.indirect_buffer[frame_idx].handle,
        0,
        2,
        size_of(vk.DrawIndexedIndirectCommand)
    )
    
    gl_VertexIndex = Undefined behavior
    
    • There is no guaranteed value.

    • Most drivers do gl_VertexIndex = 0  for all vertices.

    • In this case, you cannot rely on gl_VertexIndex  or gl_VertexID

gl_VertexID

  • Exists for compatibility (OpenGL-style)

  • In Vulkan, it is effectively the same as gl_VertexIndex

  • You generally should not rely on it being different

gl_BaseVertex

in int gl_BaseVertex;
gl_VertexIndex = gl_BaseVertex + actualIndex

gl_InstanceIndex

gl_InstanceIndex = firstInstance + (0 .. instanceCount-1)

gl_DrawID

in int gl_DrawID;
  • shaderDrawParameters  feature

  • Which draw call (inside a multi-draw / indirect call) is executing

vkCmdDrawIndexedIndirect(..., drawCount = 2);
  • Draw 0 → gl_DrawID  = 0

  • Draw 1 → gl_DrawID  = 1

Out-Of-Bounds Checks

  • GLSL does not guarantee bounds checking for descriptor arrays.

  • Vulkan validation layers cannot  reliably detect this at runtime.

  • The GPU will happily read whatever descriptor memory is adjacent.

  • Reading outside the range can:

    • Sample from garbage

    • Corrupt unrelated descriptors

    • Break other bindings (like your SSBO)

    • Cause driver crashes

  • Example:

    • I was using

    • layout(set = 0, binding = 1) uniform sampler2D texs[20];

    • This value was 20 for a long time, but I had hundred of textures being used....

    • When I created a SSBO below this binding, everything started to crash.

    • Increasing this value to 256 (the value for the cpu sparse set of textures) made the errors go away.